Option Explicit

Private Const TEST_TABLE_NAME As String = "table1"
Private Const TEST_POINT As String = "."
Private Const TEST_COL_NAME As String = "col1"
Private Const TEST_SHARE_LINE_COMMENT As String = "--aaaaaa"
Private Const TEST_SINGLE_LINE_COMMENT As String = "--bbbbb"

' ======================================================================
' 执行所有测试用例
' 这里只有格式化器的解析器中的 sql 条件对象解析方法的测试
' ======================================================================
Public Sub test_all_case()
    ' 测试: 匹配一个条件对象
    test_parseCondition

    ' 测试: 从条件运算符开始匹配一个二元条件对象
    test_parseConditionFromOperator

    ' 测试: 解析 Between 中的参数
    test_parseBetweenOpParam

    ' 测试: 解析 in 运算符的参数
    test_parseInOpParam
End Sub

' ======================================================================
' 测试通用处理
' ======================================================================

' 创建一个格式化器的 sql 解析器
Private Function createSqlFormatterAnalyzer() As SqlFormatterAnalyzer
    Dim config As SqlFormatterConfiguration
    Set config = New SqlFormatterConfiguration

    Dim result As SqlFormatterAnalyzer
    Set result = New SqlFormatterAnalyzer

    result.Init config

    Set createSqlFormatterAnalyzer = result
End Function

' 创建一个扫描器
Private Function getObjArrayRefScanner(ParamArray tokens() As Variant) As ObjArrayRefScanner
    Dim i As Integer
    Dim t As SqlToken

    Dim tokenCount As Integer
    tokenCount = UBound(tokens)

    ' 创建一个和参数数量相同的数组引用
    Dim meanTokens As IObjArrayRef
    Set meanTokens = UtilsArrayRef.createRealObjArrayRef(tokenCount)

    ' 遍历参数，并保持到数组引用
    For i = 0 To UBound(tokens)
        Set t = tokens(i)

        meanTokens.Add t
    Next

    ' 创建扫描器
    Dim scanner As ObjArrayRefScanner
    Set scanner = New ObjArrayRefScanner
    scanner.Init meanTokens

    ' return scanner
    Set getObjArrayRefScanner = scanner
End Function

' 获取一个 between 元素
Private Function getBetweenToken() As SqlToken
    Set getBetweenToken = New SqlToken
    getBetweenToken.Init "between", SqlTokenType.KEY_BETWEEN
End Function

' 获取一个 in 元素
Private Function getInToken() As SqlToken
    Set getInToken = New SqlToken
    getInToken.Init "in", SqlTokenType.KEY_IN
End Function

' 获取一个 cast 元素
Private Function getCastToken() As SqlToken
    Set getCastToken = New SqlToken
    getCastToken.Init "cast", SqlTokenType.KEY_CAST
End Function

' 创建一个字符串 token
Private Function createStrToken(ByVal v As String) As SqlToken
    Set createStrToken = New SqlToken
    createStrToken.Init "'" & v & "'", SqlTokenType.STR_STR
End Function

' case 运算符
Private Function getCaseToken() As SqlToken
    Set getCaseToken = New SqlToken
    getCaseToken.Init "case", SqlTokenType.KEY_CASE
End Function

' when 运算符
Private Function getWhenToken() As SqlToken
    Set getWhenToken = New SqlToken
    getWhenToken.Init "when", SqlTokenType.KEY_WHEN
End Function

' then 运算符
Private Function getThenToken() As SqlToken
    Set getThenToken = New SqlToken
    getThenToken.Init "then", SqlTokenType.KEY_THEN
End Function

' else 运算符
Private Function getElseToken() As SqlToken
    Set getElseToken = New SqlToken
    getElseToken.Init "else", SqlTokenType.KEY_ELSE
End Function

' end 运算符
Private Function getEndToken() As SqlToken
    Set getEndToken = New SqlToken
    getEndToken.Init "end", SqlTokenType.KEY_END
End Function

' ASC 运算符
Private Function getAscToken() As SqlToken
    Set getAscToken = New SqlToken
    getAscToken.Init "asc", SqlTokenType.KEY_ASC
End Function

' DESC 运算符
Private Function getDescToken() As SqlToken
    Set getDescToken = New SqlToken
    getDescToken.Init "desc", SqlTokenType.KEY_DESC
End Function

' || 运算符
Private Function getStrConcatToken() As SqlToken
    Set getStrConcatToken = New SqlToken
    getStrConcatToken.Init "||", SqlTokenType.OPERA_STR_CONCAT
End Function

' + 运算符
Private Function getPlusToken() As SqlToken
    Set getPlusToken = New SqlToken
    getPlusToken.Init "+", SqlTokenType.OPERA_PLUS
End Function

' - 运算符
Private Function getMinusToken() As SqlToken
    Set getMinusToken = New SqlToken
    getMinusToken.Init "-", SqlTokenType.OPERA_MINUS
End Function

' 创建一个数值元素
Private Function createNumToken(ByVal v As String) As SqlToken
    Set createNumToken = New SqlToken
    createNumToken.Init v, SqlTokenType.STR_NUM
End Function

' 获取一个 true 元素
Private Function getTrueToken() As SqlToken
    Set getTrueToken = New SqlToken
    getTrueToken.Init "true", SqlTokenType.KEY_TRUE
End Function

' 获取一个 false 元素
Private Function getFalseToken() As SqlToken
    Set getFalseToken = New SqlToken
    getFalseToken.Init "false", SqlTokenType.KEY_FALSE
End Function

' 获取一个 null 元素
Private Function getNullToken() As SqlToken
    Set getNullToken = New SqlToken
    getNullToken.Init "null", SqlTokenType.KEY_NULL
End Function

' 获取 * 元素
Private Function getAsteriskToken() As SqlToken
    Set getAsteriskToken = New SqlToken
    getAsteriskToken.Init "*", SqlTokenType.ASTERISK
End Function

' 获取 binary 关键字
Private Function getBinaryToken() As SqlToken
    Set getBinaryToken = New SqlToken
    getBinaryToken.Init "binary", SqlTokenType.KEY_BINARY
End Function

' 获取 order by 关键字
Private Function getOrderByToken() As SqlToken
    Set getOrderByToken = New SqlToken
    getOrderByToken.Init "order by", SqlTokenType.KEY_ORDER_BY
End Function

' 获取 group by 关键字
Private Function getGroupByToken() As SqlToken
    Set getGroupByToken = New SqlToken
    getGroupByToken.Init "group by", SqlTokenType.KEY_GROUP_BY
End Function

' 获取 with 关键字
Private Function getWithToken() As SqlToken
    Set getWithToken = New SqlToken
    getWithToken.Init "with", SqlTokenType.KEY_WITH
End Function

' 获取 ROLLUP 关键字
Private Function getRollupToken() As SqlToken
    Set getRollupToken = New SqlToken
    getRollupToken.Init "rollup", SqlTokenType.KEY_ROLLUP
End Function

' 获取 offset 关键字
Private Function getOffsetToken() As SqlToken
    Set getOffsetToken = New SqlToken
    getOffsetToken.Init "offset", SqlTokenType.KEY_OFFSET
End Function

' 获取 limit 关键字
Private Function getLimitToken() As SqlToken
    Set getLimitToken = New SqlToken
    getLimitToken.Init "limit", SqlTokenType.KEY_LIMIT
End Function

' 获取 is 关键字
Private Function getIsToken() As SqlToken
    Set getIsToken = New SqlToken
    getIsToken.Init "is", SqlTokenType.KEY_IS
End Function

' 获取 not 关键字
Private Function getNotToken() As SqlToken
    Set getNotToken = New SqlToken
    getNotToken.Init "not", SqlTokenType.KEY_NOT
End Function

' 获取 union 关键字
Private Function getUnionToken() As SqlToken
    Set getUnionToken = New SqlToken
    getUnionToken.Init "union", SqlTokenType.KEY_UNION
End Function

' 获取 all 关键字
Private Function getAllToken() As SqlToken
    Set getAllToken = New SqlToken
    getAllToken.Init "all", SqlTokenType.KEY_ALL
End Function

' 获取 join 关键字
Private Function getJoinToken() As SqlToken
    Set getJoinToken = New SqlToken
    getJoinToken.Init "join", SqlTokenType.KEY_JOIN
End Function

' 获取 left 关键字
Private Function getLeftToken() As SqlToken
    Set getLeftToken = New SqlToken
    getLeftToken.Init "left", SqlTokenType.KEY_LEFT
End Function

' 获取 right 关键字
Private Function getRightToken() As SqlToken
    Set getRightToken = New SqlToken
    getRightToken.Init "right", SqlTokenType.KEY_RIGHT
End Function

' 获取 inner 关键字
Private Function getInnerToken() As SqlToken
    Set getInnerToken = New SqlToken
    getInnerToken.Init "inner", SqlTokenType.KEY_INNER
End Function

' 获取 cross 关键字
Private Function getCrossToken() As SqlToken
    Set getCrossToken = New SqlToken
    getCrossToken.Init "cross", SqlTokenType.KEY_CROSS
End Function

' 获取 out 关键字
Private Function getOuterToken() As SqlToken
    Set getOuterToken = New SqlToken
    getOuterToken.Init "outer", SqlTokenType.KEY_OUTER
End Function

' 获取 use 关键字
Private Function getUseToken() As SqlToken
    Set getUseToken = New SqlToken
    getUseToken.Init "use", SqlTokenType.KEY_USE
End Function

' 获取 index 关键字
Private Function getIndexToken() As SqlToken
    Set getIndexToken = New SqlToken
    getIndexToken.Init "index", SqlTokenType.KEY_INDEX
End Function

' 获取 ignore 关键字
Private Function getIgnoreToken() As SqlToken
    Set getIgnoreToken = New SqlToken
    getIgnoreToken.Init "ignore", SqlTokenType.KEY_IGNORE
End Function

' 获取 key 关键字
Private Function getKeyToken() As SqlToken
    Set getKeyToken = New SqlToken
    getKeyToken.Init "key", SqlTokenType.KEY_KEY
End Function

' 获取 for 关键字
Private Function getForToken() As SqlToken
    Set getForToken = New SqlToken
    getForToken.Init "for", SqlTokenType.KEY_FOR
End Function

' 获取 force 关键字
Private Function getForceToken() As SqlToken
    Set getForceToken = New SqlToken
    getForceToken.Init "force", SqlTokenType.KEY_FORCE
End Function

' 获取 as 关键字
Private Function getAsToken() As SqlToken
    Set getAsToken = New SqlToken
    getAsToken.Init "as", SqlTokenType.KEY_AS
End Function

' 获取左括号
Private Function getLeftParenthesisToken() As SqlToken
    Set getLeftParenthesisToken = New SqlToken
    getLeftParenthesisToken.Init "(", SqlTokenType.LEFT_PARENTHESIS
End Function

' 获取右括号
Private Function getRightParenthesisToken() As SqlToken
    Set getRightParenthesisToken = New SqlToken
    getRightParenthesisToken.Init ")", SqlTokenType.RIGHT_PARENTHESIS
End Function

' 获取 partition 关键字
Private Function getPartitionToken() As SqlToken
    Set getPartitionToken = New SqlToken
    getPartitionToken.Init "partition", SqlTokenType.KEY_PARTITION
End Function

' 获取逻辑运算符: and
Private Function getAndToken() As SqlToken
    Set getAndToken = New SqlToken
    getAndToken.Init "and", SqlTokenType.KEY_AND
End Function

' 获取一个条件运算符: <=
Private Function getLeConditionOperator() As SqlToken
    Set getLeConditionOperator = New SqlToken
    getLeConditionOperator.Init "<=", SqlTokenType.OPERA_LE
End Function

' 获取一个任意名称的 STR_NAME token
Private Function getStrNameToken(ByRef s As String) As SqlToken
    Set getStrNameToken = New SqlToken
    getStrNameToken.Init s, SqlTokenType.STR_NAME
End Function

' 获取表名 token
Private Function getTableNameToken() As SqlToken
    Set getTableNameToken = New SqlToken
    getTableNameToken.Init TEST_TABLE_NAME, SqlTokenType.STR_NAME
End Function

' 获取 [.] token
Private Function getPointToken() As SqlToken
    Set getPointToken = New SqlToken
    getPointToken.Init TEST_POINT, SqlTokenType.POINT
End Function

' 获取列名 token
Private Function getColNameToken() As SqlToken
    Set getColNameToken = New SqlToken
    getColNameToken.Init TEST_COL_NAME, SqlTokenType.STR_NAME
End Function

' 创建和前一个元素在同一行的注释
Private Function createShareLineCommentToken(ByRef v As String) As SqlToken
    Set createShareLineCommentToken = New SqlToken
    createShareLineCommentToken.Init v, SqlTokenType.SHARE_LINE_COMMENT
End Function

' 获取一个单行的注释
Private Function createSingleLineCommentToken(ByRef v As String) As SqlToken
    Set createSingleLineCommentToken = New SqlToken
    createSingleLineCommentToken.Init v, SqlTokenType.SINGLE_LINE_COMMENT
End Function

' 获取和前一个元素在同一行的注释
Private Function getShareLineCommentToken() As SqlToken
    Set getShareLineCommentToken = New SqlToken
    getShareLineCommentToken.Init TEST_SHARE_LINE_COMMENT, SqlTokenType.SHARE_LINE_COMMENT
End Function

' 获取一个单行的注释
Private Function getSingleLineCommentToken() As SqlToken
    Set getSingleLineCommentToken = New SqlToken
    getSingleLineCommentToken.Init TEST_SINGLE_LINE_COMMENT, SqlTokenType.SINGLE_LINE_COMMENT
End Function

' 获取一个逗号的元素
Private Function getCommaToken() As SqlToken
    Set getCommaToken = New SqlToken
    getCommaToken.Init ",", SqlTokenType.COMMA
End Function

' 生成一个函数名称元素
Private Function createFuncNameToken(ByVal funcName As String) As SqlToken
    Set createFuncNameToken = New SqlToken
    createFuncNameToken.Init funcName, SqlTokenType.STR_FUNC
End Function

' 获取 CURRENT_TIMESTAMP 元素
Private Function getCurrentTimestampToken() As SqlToken
    Set getCurrentTimestampToken = New SqlToken
    getCurrentTimestampToken.Init "CURRENT_TIMESTAMP", SqlTokenType.CONST_CURRENT_TIMESTAMP
End Function

' 获取一个没有边界括号的多行元素
' 多行连接的结果:
' col1 & ","
' col2
Private Function getMultiLineElemNoParenthesis() As ISqlFormatterElem
    Dim g As SqlFormatterCommaDeliMultiElem
    Set g = New SqlFormatterCommaDeliMultiElem

    Dim elem As ISqlFormatterElem
    Set elem = g

    ' 1. 增加一个单行元素
    elem.addComponent getStrNameToken("col1")

    ' 2. 手动设置为多行元素，仍然是单行
    g.setNeedMultiLineWhenHasComma True

    ' 3. 在末尾添加一个逗号元素，仍然是单行的
    elem.addComponent getCommaToken

    ' 4. 再增加一个单行元素，变成多行的
    elem.addComponent getStrNameToken("col2")

    Set getMultiLineElemNoParenthesis = elem
End Function

' 获取一个没有边界括号的多行元素
' 多行连接的结果:
' col1 & ","
' col2 --aaaaaa
Private Function getMultiLineElemNoParenthesisEndWithComment() As ISqlFormatterElem
    Dim g As SqlFormatterCommaDeliMultiElem
    Set g = New SqlFormatterCommaDeliMultiElem

    Dim elem As ISqlFormatterElem
    Set elem = g

    ' 1. 增加一个单行元素
    elem.addComponent getStrNameToken("col1")

    ' 2. 手动设置为多行元素，仍然是单行
    g.setNeedMultiLineWhenHasComma True

    ' 3. 在末尾添加一个逗号元素，仍然是单行的
    elem.addComponent getCommaToken

    ' 4. 再增加一个单行元素，变成多行的
    elem.addComponent getStrNameToken("col2")

    ' 5. 增加一个和前一个元素同一行的注释
    elem.addComponent getShareLineCommentToken

    Set getMultiLineElemNoParenthesisEndWithComment = elem
End Function

' ======================================================================
' Sql 解析方法的测试
' ======================================================================

' 测试: 匹配一个条件对象
Private Sub test_parseCondition()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack

    ' ==============================
    ' 0. 扫描器内: 空扫描器
    '    测试无法解析出任何内容 + logicOp 非空 = 只返回 logicOp
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getUnionToken)
    scanner.toNext
    ' 2. 执行测试方法
    Set token = getAndToken
    Set elem = analyzer.parseCondition(scanner, Nothing, token)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 1. 扫描器内: UNION
    '    测试无法解析出任何内容 + logicOp 是空 = 不返回任何内容
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert elem Is Nothing
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    ' ==============================
    ' 2. 扫描器内: UNION
    '    测试无法解析出任何内容，并返回 logicOp
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getUnionToken)
    ' 2. 执行测试方法
    Set token = getAndToken
    Set elem = analyzer.parseCondition(scanner, Nothing, token)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    ' ==============================
    ' 3. 扫描器内: STR_NAME
    '    没有 logicOp，测试只有一个元素的条件
    ' ==============================
    ' 1. 创建扫描器
    Set token = getStrNameToken("x1")
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 4. 扫描器内: STR_NAME
    '    有 logicOp，测试只有一个元素的条件
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "and x1"

    ' ==============================
    ' 5. 扫描器内: STR_NAME + UNION(强制停止)
    '    有 logicOp，测试只有一个元素的条件 + 第一个元素后不是运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "and x1"

    ' ==============================
    ' 6. 扫描器内: STR_NAME + 一个注释 + UNION(强制停止)
    '    有 logicOp，测试只有一个元素的条件 + 第一个元素后不是运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), createShareLineCommentToken("--c1"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "and x1 --c1"

    ' ==============================
    ' 7. 扫描器内: STR_NAME + <=
    '    有 logicOp
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getLeConditionOperator)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "and x1 <="

    ' ==============================
    ' 8. 扫描器内: STR_NAME + <= + UNION
    '    没有 logicOp
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getLeConditionOperator, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "x1 <="

    ' ==============================
    ' 8. 扫描器内: STR_NAME + <= + STR_NAME
    '    没有 logicOp
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getLeConditionOperator, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "x1 <= x2"

    ' ==============================
    ' 9. 扫描器内: STR_NAME + <= + STR_NAME
    '    有 logicOp
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getLeConditionOperator, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "and x1 <= x2"

    ' ==============================
    ' 10. 扫描器内: STR_NAME + <= + STR_NAME + UNION(强制停止)
    '    有 logicOp
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getLeConditionOperator, _
        getStrNameToken("x2"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "and x1 <= x2"

    ' ==============================
    ' 11. 扫描器内: STR_NAME + Not + <= + STR_NAME + UNION(强制停止)
    '    有 logicOp + 包含 Not 的运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getNotToken, getLeConditionOperator, _
        getStrNameToken("x2"), getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "and x1 not <= x2"

    ' ==============================
    ' 12. 扫描器内: STR_NAME + Not + STR_NAME
    '     包含 Not 但是不是运算符
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getNotToken, _
        getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_NOT
    Debug.Assert elem.toString(config) = "and x1"

    ' ==============================
    ' 13. 扫描器内: STR_NAME + <= + STR_NAME + )
    '     左括号栈: 一个左括号
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getLeConditionOperator, _
        getStrNameToken("x2"), getRightParenthesisToken)
    Set stack = New  ObjStack
    stack.push getLeftParenthesisToken
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, stack, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(and x1 <= x2)"

    ' ==============================
    ' 14. 扫描器内:
    '                  + case
    '                  + when + STR_NAME + Then + STR_NAME
    '                  + end
    '              + <=
    '                  + STR_NAME + PLUS
    '                      + case
    '                      + when + STR_NAME + Then + STR_NAME
    '                      + end
    '     多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken, _
        _
        getLeConditionOperator, _
        _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseCondition(scanner, Nothing, getAndToken)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    elem.setPrefixSpaceCount 0, config
    Debug.Assert strArr.Count = 8
    Debug.Assert strArr.Item(0) = "and case"
    Debug.Assert strArr.Item(1) = "        when x1 then x2"
    Debug.Assert strArr.Item(2) = "    end"
    Debug.Assert strArr.Item(3) = "    <="
    Debug.Assert strArr.Item(4) = "    y1 +"
    Debug.Assert strArr.Item(5) = "        case"
    Debug.Assert strArr.Item(6) = "            when x3 then x4"
    Debug.Assert strArr.Item(7) = "        end"

End Sub

' 测试: 从条件运算符开始匹配一个二元条件对象
Private Sub test_parseConditionFromOperator()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef
    Dim stack As ObjStack
    Dim condition As SqlFormatterTwoElemCondition

    ' ==============================
    ' 1. 扫描器用完
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getUnionToken)
    scanner.toNext
    ' 2. 创建条件对象
    Set condition = New SqlFormatterTwoElemCondition
    condition.ISqlFormatterCondition_setLogicOperator getAndToken
    condition.ISqlFormatterElem_addComponent getStrNameToken("x1")
    condition.ISqlFormatterCondition_setConditionOperator getLeConditionOperator
    ' 3. 执行测试方法
    Set elem = analyzer.parseConditionFromOperator(scanner, Nothing, condition, SqlTokenType.OPERA_LE)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "and x1 <="

    ' ==============================
    ' 2. 扫描器: STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x2"))
    ' 2. 创建条件对象
    Set condition = New SqlFormatterTwoElemCondition
    condition.ISqlFormatterCondition_setLogicOperator getAndToken
    condition.ISqlFormatterElem_addComponent getStrNameToken("x1")
    condition.ISqlFormatterCondition_setConditionOperator getLeConditionOperator
    ' 3. 执行测试方法
    Set elem = analyzer.parseConditionFromOperator(scanner, Nothing, condition, SqlTokenType.OPERA_LE)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "and x1 <= x2"

    ' ==============================
    ' 3. 扫描器: STR_NAME + UNION(强制停止)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x2"), getUnionToken)
    ' 2. 创建条件对象
    Set condition = New SqlFormatterTwoElemCondition
    condition.ISqlFormatterCondition_setLogicOperator getAndToken
    condition.ISqlFormatterElem_addComponent getStrNameToken("x1")
    condition.ISqlFormatterCondition_setConditionOperator getLeConditionOperator
    ' 3. 执行测试方法
    Set elem = analyzer.parseConditionFromOperator(scanner, Nothing, condition, SqlTokenType.OPERA_LE)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "and x1 <= x2"

    ' ==============================
    ' 4. 扫描器: STR_NAME + )
    '    左括号栈
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x2"),getRightParenthesisToken)
    Set stack = New ObjStack
    stack.push getLeftParenthesisToken
    ' 2. 创建条件对象
    Set condition = New SqlFormatterTwoElemCondition
    condition.ISqlFormatterCondition_setLogicOperator getAndToken
    condition.ISqlFormatterElem_addComponent getStrNameToken("x1")
    condition.ISqlFormatterCondition_setConditionOperator getLeConditionOperator
    ' 3. 执行测试方法
    Set elem = analyzer.parseConditionFromOperator(scanner, stack, condition, SqlTokenType.OPERA_LE)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "(and x1 <= x2)"

    ' ==============================
    ' 5. 扫描器: STR_NAME + and + STR_NAME + UNION
    '    条件运算符 = between
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getAndToken, getStrNameToken("x2"), _
        getUnionToken)
    ' 2. 创建条件对象
    Set condition = New SqlFormatterTwoElemCondition
    condition.ISqlFormatterCondition_setLogicOperator getAndToken
    condition.ISqlFormatterElem_addComponent getStrNameToken("y1")
    condition.ISqlFormatterCondition_setConditionOperator getBetweenToken
    ' 3. 执行测试方法
    Set elem = analyzer.parseConditionFromOperator(scanner, Nothing, condition, SqlTokenType.KEY_BETWEEN)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "and y1 between x1 and x2"

    ' ==============================
    ' 6. 扫描器: ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + ) + UNION
    '    条件运算符 = in
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getCommaToken, getStrNameToken("x3"), _
        getRightParenthesisToken, getUnionToken)
    ' 2. 创建条件对象
    Set condition = New SqlFormatterTwoElemCondition
    condition.ISqlFormatterCondition_setLogicOperator getAndToken
    condition.ISqlFormatterElem_addComponent getStrNameToken("y1")
    condition.ISqlFormatterCondition_setConditionOperator getInToken
    ' 3. 执行测试方法
    Set elem = analyzer.parseConditionFromOperator(scanner, Nothing, condition, SqlTokenType.KEY_IN)
    ' 4. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "and y1 in (x1, x2, x3)"

    ' ==============================
    ' 7. 扫描器内: STR_NAME + [+]
    '                  + case
    '                  + when + STR_NAME + Then + STR_NAME
    '                  + end
    '     多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken)
    ' 2. 创建条件对象
    Set condition = New SqlFormatterTwoElemCondition
    condition.ISqlFormatterCondition_setLogicOperator getAndToken
    condition.ISqlFormatterElem_addComponent getStrNameToken("x1")
    condition.ISqlFormatterCondition_setConditionOperator getLeConditionOperator
    ' 2. 执行测试方法
    Set elem = analyzer.parseConditionFromOperator(scanner, stack, condition, SqlTokenType.OPERA_LE)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterTwoElemCondition
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 4
    Debug.Assert strArr.Item(0) = "and x1 <= y1 +"
    Debug.Assert strArr.Item(1) = "              case"
    Debug.Assert strArr.Item(2) = "                  when x3 then x4"
    Debug.Assert strArr.Item(3) = "              end"

End Sub

' 测试: 解析 Between 中的参数
Private Sub test_parseBetweenOpParam()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: UNION (无法解析)
    ' ==============================
    ' 1. 创建扫描器
    Set token = getUnionToken
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is Nothing
    Debug.Assert scanner.hasNext = True
    Debug.Assert scanner.getNext Is token

    ' ==============================
    ' 2. 扫描器内: STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set token = getStrNameToken("x1")
    Set scanner = getObjArrayRefScanner(token)
    ' 2. 执行测试方法
    Set elem = analyzer.parseBetweenOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is token
    Debug.Assert scanner.hasNext = False

    ' ==============================
    ' 3. 扫描器内: STR_NAME + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set token = getStrNameToken("x1")
    Set scanner = getObjArrayRefScanner(token, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseBetweenOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is Nothing
    Debug.Assert scanner.hasNext = True
    Debug.Assert scanner.getNext Is token

    ' ==============================
    ' 4. 扫描器内: STR_NAME + and
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getAndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseBetweenOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterBetweenRange
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "x1 and"

    ' ==============================
    ' 5. 扫描器内: STR_NAME + and + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getAndToken, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseBetweenOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterBetweenRange
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "x1 and"

    ' ==============================
    ' 6. 扫描器内: STR_NAME + and + STR_NAME
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getAndToken, getStrNameToken("x2"))
    ' 2. 执行测试方法
    Set elem = analyzer.parseBetweenOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterBetweenRange
    Debug.Assert scanner.hasNext = False
    Debug.Assert elem.toString(config) = "x1 and x2"

    ' ==============================
    ' 7. 扫描器内: STR_NAME + and + STR_NAME + UNION
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getStrNameToken("x1"), getAndToken, getStrNameToken("x2"), _
        getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseBetweenOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterBetweenRange
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Debug.Assert elem.toString(config) = "x1 and x2"

    ' ==============================
    ' 8. 扫描器内:
    '                  + case
    '                  + when + STR_NAME + Then + STR_NAME
    '                  + end
    '              + And
    '                  + STR_NAME + PLUS
    '                      + case
    '                      + when + STR_NAME + Then + STR_NAME
    '                      + end
    '     多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken, _
        _
        getAndToken, _
        _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseBetweenOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterBetweenRange
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 8
    Debug.Assert strArr.Item(0) = "case"
    Debug.Assert strArr.Item(1) = "    when x1 then x2"
    Debug.Assert strArr.Item(2) = "end"
    Debug.Assert strArr.Item(3) = "and"
    Debug.Assert strArr.Item(4) = "y1 +"
    Debug.Assert strArr.Item(5) = "    case"
    Debug.Assert strArr.Item(6) = "        when x3 then x4"
    Debug.Assert strArr.Item(7) = "    end"

End Sub

' 测试: 解析 in 运算符的参数
Private Sub test_parseInOpParam()
    Dim analyzer As SqlFormatterAnalyzer
    Set analyzer = createSqlFormatterAnalyzer

    Dim config As SqlFormatterConfiguration
    Set config = analyzer.getConfig

    Dim token As SqlToken
    Dim scanner As ObjArrayRefScanner
    Dim elem As ISqlFormatterElem
    Dim strArr As IPrimitiveArrayRef

    ' ==============================
    ' 1. 扫描器内: UNION (开头不是:左括号，返回 null)
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert elem Is Nothing
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    ' ==============================
    ' 2. 扫描器内: ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + )
    '     测试: 开头不是注释的情况
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getCommaToken, getStrNameToken("x3"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False
    Set strArr = elem.toStringArray(config)
    Debug.Assert elem.toString(config) = "(x1, x2, x3)"

    ' ==============================
    ' 3. 扫描器内: ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + ) + UNION
    '     测试: 开头不是注释的情况
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getCommaToken, getStrNameToken("x3"), _
        getRightParenthesisToken, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION
    Set strArr = elem.toStringArray(config)
    Debug.Assert elem.toString(config) = "(x1, x2, x3)"

    ' ==============================
    ' 4. 扫描器内: 一个注释 + ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + )
    '     测试: 开头是注释的情况
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        createShareLineCommentToken("--c1"), _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getCommaToken, getStrNameToken("x3"), _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "--c1"
    Debug.Assert strArr.Item(1) = "(x1, x2, x3)"

    ' ==============================
    ' 5. 扫描器内: 一个注释 + ( + STR_NAME + [,] + STR_NAME + [,] + STR_NAME + ) + UNION
    '     测试: 开头是注释的情况
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner( _
        createShareLineCommentToken("--c1"), _
        getLeftParenthesisToken, _
        getStrNameToken("x1"), getCommaToken, getStrNameToken("x2"), _
        getCommaToken, getStrNameToken("x3"), _
        getRightParenthesisToken, getUnionToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterContainerCol
    Debug.Assert scanner.hasNext = True
    Set token = scanner.getNext
    Debug.Assert token.tokenType = SqlTokenType.KEY_UNION

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 2
    Debug.Assert strArr.Item(0) = "--c1"
    Debug.Assert strArr.Item(1) = "(x1, x2, x3)"

    ' ==============================
    ' 6. 扫描器内: (
    '                  + case
    '                  + when + STR_NAME + Then + STR_NAME
    '                  + end
    '              + [,]
    '                  + STR_NAME + [+]
    '                      + case
    '                      + when + STR_NAME + Then + STR_NAME
    '                      + end
    '              + )
    '     多行元素测试 + 表达式
    ' ==============================
    ' 1. 创建扫描器
    Set scanner = getObjArrayRefScanner(getLeftParenthesisToken, _
        _
        getCaseToken, _
        getWhenToken, getStrNameToken("x1"), getThenToken, getStrNameToken("x2"), _
        getEndToken, _
        _
        getCommaToken, _
        _
        getStrNameToken("y1"), getPlusToken, _
        getCaseToken, _
        getWhenToken, getStrNameToken("x3"), getThenToken, getStrNameToken("x4"), _
        getEndToken, _
        _
        getRightParenthesisToken)
    ' 2. 执行测试方法
    Set elem = analyzer.parseInOpParam(scanner)
    ' 3. 检查结果
    Debug.Assert TypeOf elem Is SqlFormatterCommaDeliMultiElem
    Debug.Assert scanner.hasNext = False

    Set strArr = elem.toStringArray(config)
    Debug.Assert strArr.Count = 9
    Debug.Assert strArr.Item(0) = "("
    Debug.Assert strArr.Item(1) = "    case"
    Debug.Assert strArr.Item(2) = "        when x1 then x2"
    Debug.Assert strArr.Item(3) = "    end,"
    Debug.Assert strArr.Item(4) = "    y1 +"
    Debug.Assert strArr.Item(5) = "        case"
    Debug.Assert strArr.Item(6) = "            when x3 then x4"
    Debug.Assert strArr.Item(7) = "        end"
    Debug.Assert strArr.Item(8) = ")"

End Sub
